package com.im.tcp.server;

import com.im.codec.WebSocketMessageDecoder;
import com.im.codec.WebSocketMessageEncoder;
import com.im.tcp.config.TcpConfig;
import com.im.tcp.feign.FeignMessageService;
import com.im.tcp.handler.HeartBeatHandler;
import com.im.tcp.handler.NettyServerHandler;
import com.im.tcp.publish.MqMessageProducer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Slf4j
@Component
public class LimWebSocketServer {

    @Autowired
    private TcpConfig nettyConfig;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private FeignMessageService feignMessageService;
    @Autowired
    private MqMessageProducer mqMessageProducer;

    private EventLoopGroup mainGroup;
    private EventLoopGroup subGroup;
    private ServerBootstrap server;

    @PostConstruct
    public void start() {
        mainGroup = new NioEventLoopGroup(nettyConfig.getBossThreadSize()); // 处理客户端请求的 boss 线程组
        subGroup = new NioEventLoopGroup(nettyConfig.getWorkThreadSize()); // 处理接收到的连接的 worker 线程组
        server = new ServerBootstrap(); // 创建服务器的引导对象
        server.group(mainGroup, subGroup).channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务器端用于接受客户端连接的队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 表示是否允许重用同一端口号
                .option(ChannelOption.TCP_NODELAY, true) // 表示是否禁用Nagle算法
                .option(ChannelOption.SO_KEEPALIVE, true) // 表示是否开启TCP的心跳检测机制
                .childHandler(new ChannelInitializer<SocketChannel>() { // 设置每个新连接的ChannelPipeline
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 添加多个ChannelHandler，用于处理新连接上的数据
                        // 每个连接的数据都将按照设置的ChannelHandler的顺序进行处理
                        ChannelPipeline pipeline = ch.pipeline();
                        // websocket 基于http协议，所以要有http解码器
                        pipeline.addLast("http-codec", new HttpServerCodec());
                        // 对写大数据流的支持
                        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                        // 几乎在netty的编程中，都会使用到此handler
                        pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
                        /**
                         * websocket 服务器处理的协议，用于指定给客户端连接访问的路由 : /ws
                         * 本handler会帮你处理一些繁重的复杂的事
                         * 会帮你处理握手动作： handshaking（close, ping, pong） ping + pong = 心跳
                         * 对于websocket来讲，都是以frames进行传输的，不同的数据类型对应的frames也不同
                         */
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                        /**
                         * 将 WebSocketMessageDecoder、WebSocketMessageEncoder 和 NettyServerHandler 添加到通道处理器链中
                         */
                        pipeline.addLast(new WebSocketMessageDecoder()); // 添加 WebSocket 消息解码器
                        pipeline.addLast(new WebSocketMessageEncoder()); // 添加 WebSocket 消息编码器
                        pipeline.addLast(new HeartBeatHandler(nettyConfig.getHeartBeatTime())); // 心跳处理器，用于处理客户端发来的心跳消息
                        pipeline.addLast(new NettyServerHandler(redissonClient, feignMessageService, nettyConfig, mqMessageProducer)); // 添加 Netty 服务器处理器，用于处理客户端发送的消息
                    }
                });

        server.bind(nettyConfig.getWebSocketPort());
        log.info("WebSocket服务启动成功");
    }

}
